import dayjs from "../libs/dayjs";
import { Pages } from "./to/pages/Pages";
import { currentPage } from "./to/pages/utils/consts";

export type Token<T> = string & { __type?: T }
export class AstContext {
    private cache: any = {}
    private storage: AutoJs.Storage;
    constructor(private d: typeof dayjs, private _parent?: AstContext) {
        this.storage = storages.create(context.getPackageName())
    }
    get parent() {
        return this._parent
    }
    isPage(page: Pages) {
        return this.get(currentPage) === page
    }
    getText(node: AutoJs.UiObject): string | undefined {
        const desc = node.desc()
        if (desc) return desc;
        const text = node.text()
        if (text) return text;
        const childCount = node.childCount()
        for (let i = 0; i < childCount; i++) {
            const child = node.child(i)
            if (child) {
                const childText = this.getText(child)
                if (childText) return childText
            }
        }
        return;
    }
    isText(ast: AutoJs.UiObject, text: string): boolean {
        return ast.text() === text;
    }
    isSid(ast: AutoJs.UiObject | null | undefined, text: string): boolean {
        const fullid = ast?.id() || ``;
        if (fullid) {
            const [prev, id] = fullid.split('/')
            return id === text
        }
        return false
    }
    idEndWidths(ast: AutoJs.UiObject | null | undefined, text: string) {
        const id = ast?.id() || ''
        return id.endsWith(text)
    }
    formatData(date: Date) {
        return this.d(date).format('YYYY-MM-DD HH:mm:ss')
    }
    tosecond(text: string) {
        if (!text) return 0
        const timeReg = /(\d\d):(\d\d)/
        const match = text.match(timeReg)
        if (match && match.length > 1) {
            const [, m1, m2] = match
            return parseInt(m1) * 60 + parseInt(m2)
        }
        return 0;
    }
    toSecondDate(second: number) {
        return new Date(new Date().getTime() + second * 1000)
    }
    getTime() {
        return this.d().toDate().getTime()
    }
    getSecond() {
        return Math.floor(this.d().toDate().getTime() / 1000);
    }
    get<T>(key: Token<T>) {
        return this.cache[key] as T;
    }
    call(key: string, ...args: any[]) {
        const method = this.cache[key] as Function;
        if (method) {
            method(...args)
            return true;
        }
        return false;
    }
    private __all: boolean = true;
    setCollectAll(all: boolean = false) {
        this.__all = all;
    }
    collectAll() {
        return this.__all;
    }
    set<T>(key: Token<T>, value: T) {
        this.cache[key] = value;
    }
    clear() {
        this.cache = {}
    }
    captureScreen() {
        return captureScreen()
    }
    captcha() {
        const image = captureScreen()
        const point = images.findMultiColors(image, "#282627", [[0, 38, "#0c0c0e"], [0, 67, "#202022"], [0, 107, "#212227"], [107, 106, "#2b1917"], [107, 106, "#2b1917"], [108, 70, "#545051"], [133, 61, "#221e1f"], [131, 40, "#726e6f"], [106, 19, "#2c2a2b"], [104, 2, "#282828"], [73, 0, "#282828"], [53, 45, "#222222"], [53, 63, "#212121"]],
            {
                threshold: 40,
                similarity: 0.7
            }
        )
        if (point) {
            const { x } = point;
            const captcha = selector().visibleToUser().textStartsWith('cutPic?').findOnce()
            if (captcha) {
                const bounds = captcha.bounds()
                const targetX = bounds.left;
                const decX = x - targetX;
                if (decX > 0) {
                    const drag = selector().visibleToUser().text('captcha').findOnce()
                    if (drag) {
                        const child = drag.child(0)
                        if (child) {
                            const dragNode = this.bfsOne(child, it => it.clickable())
                            if (dragNode) {
                                const bounds = dragNode.bounds()

                                console.log([
                                    bounds.centerX(), bounds.centerY(), x, bounds.centerY()
                                ])
                                swipe(bounds.centerX(), bounds.centerY(), x, bounds.centerY(), 1500)
                            }
                        }
                    }
                }
            }
        }
    }
    isRed(bounds: AutoJs.Rect) {
        try {
            const image = captureScreen()
            const point = images.findMultiColors(image,
                "#fea37d", [[5, 0, "#fece84"], [11, -5, "#fdee89"], [11, 5, "#feef89"], [15, 0, "#feef89"], [20, 0, "#fec282"], [23, 0, "#d4806c"], [14, 10, "#ff386b"], [11, -11, "#fd386a"], [4, -8, "#fd386a"], [18, -6, "#fd386a"], [5, 11, "#ff386b"], [16, 10, "#ff386b"]],
                {
                    region: [bounds.left, bounds.top, bounds.width(), bounds.height()],
                    threshold: 50,
                    similarity: 0.6
                }
            )
            return !!point;
        } catch (e) {
            console.log(e)
            return false;
        }
    }
    all() {
        return this.cache
    }
    log(msg: string) {
        events.emit("log", msg)
    }
    launchApp(name: string) {
        app.launchPackage(name)
    }
    findInParent(node: AutoJs.UiObject | null | undefined, filter: (node: AutoJs.UiObject) => boolean): AutoJs.UiObject | undefined {
        if (!node) return undefined
        if (filter(node)) return node;
        let parent = node.parent()
        node.recycle()
        if (parent) {
            return this.findInParent(parent, filter)
        }
        return undefined
    }
    filterOneChildren(root: AutoJs.UiObject | null | undefined, filter: (node: AutoJs.UiObject) => boolean) {
        if (!root) return;
        const result: AutoJs.UiObject[] = []
        const count = root.childCount()
        for (let i = 0; i < count; i++) {
            const node = root.child(i)
            if (node) {
                if (filter(node)) {
                    result.push(node)
                } else {
                    node.recycle()
                }
            }
        }
        if (result && result.length > 0) return result[0]
        return
    }
    filterChildren(root: AutoJs.UiObject | null | undefined, filter: (node: AutoJs.UiObject) => boolean) {
        if (!root) return [];
        const result: AutoJs.UiObject[] = []
        const count = root.childCount()
        for (let i = 0; i < count; i++) {
            const node = root.child(i)
            if (node) {
                if (filter(node)) {
                    result.push(node)
                } else {
                    node.recycle()
                }
            }
        }
        return result;
    }
    bfsOne(root: AutoJs.UiObject, filter: (node: AutoJs.UiObject) => boolean) {
        if (filter(root)) return root;
        const queue: AutoJs.UiObject[] = []
        queue.push(root)
        let target: AutoJs.UiObject | null = null;
        while (queue.length > 0) {
            const top = queue.pop()
            if (!top) continue
            const isTarget = filter(top)
            if (isTarget) {
                target = top;
                break;
            }
            const childCount = top.childCount()
            for (let i = 0; i < childCount; i++) {
                const child = top.child(i)
                if (!child) continue
                queue.push(child)
            }
            if (!isTarget && top !== root) {
                top.recycle()
            }
        }
        return target;
    }
    bfs(root: AutoJs.UiObject, filter: (node: AutoJs.UiObject) => boolean) {
        const result: AutoJs.UiObject[] = []
        const queue: AutoJs.UiObject[] = []
        queue.push(root)
        while (queue.length > 0) {
            const top = queue.pop()
            if (!top) continue
            const isTarget = filter(top)
            if (isTarget) {
                result.push(top)
            }
            const childCount = top.childCount()
            for (let i = 0; i < childCount; i++) {
                const child = top.child(i)
                if (!child) continue
                queue.push(child)
            }
            if (!isTarget && top !== root) {
                top.recycle()
            }
        }
        return result
    }
    dfsOne(root: AutoJs.UiObject, filter: (node: AutoJs.UiObject) => boolean): AutoJs.UiObject | null {
        if (filter(root)) return root;
        const stack: AutoJs.UiObject[] = []
        let target: AutoJs.UiObject | null = null;
        stack.push(root)
        while (stack.length > 0) {
            const parent = stack.pop()
            if (!parent) continue
            const childCount = parent.childCount()
            for (let i = childCount - 1; i > 0; i--) {
                const child = parent.child(i)
                if (!child) continue
                stack.push(child)
            }
            if (filter(parent)) {
                target = parent;
                break;
            } else {
                if (parent !== root) {
                    parent.recycle()
                }
            }
        }
        return target;
    }
    dfs(root: AutoJs.UiObject, filter: (node: AutoJs.UiObject) => boolean) {
        const result: AutoJs.UiObject[] = []
        const stack: AutoJs.UiObject[] = []
        stack.push(root)
        while (stack.length > 0) {
            const parent = stack.pop()
            if (!parent) continue
            const childCount = parent.childCount()
            for (let i = childCount - 1; i > 0; i--) {
                const child = parent.child(i)
                if (!child) continue
                stack.push(child)
            }
            if (filter(parent)) {
                result.push(parent)
            } else {
                if (parent !== root) {
                    parent.recycle()
                }
            }
        }
        return result
    }
    filter(root: AutoJs.UiObject | null | undefined, filter: (node: AutoJs.UiObject) => boolean): AutoJs.UiObject[] {
        if (!root) return [];
        const result: AutoJs.UiObject[] = []
        const queue: AutoJs.UiObject[] = []
        queue.push(root)
        while (queue.length > 0) {
            const top = queue.pop()
            if (!top) continue
            const isTarget = filter(top)
            if (isTarget) {
                result.push(top)
            }
            const childCount = top.childCount()
            for (let i = 0; i < childCount; i++) {
                const child = top.child(i)
                if (!child) continue
                queue.push(child)
            }
            if (!isTarget && top !== root) {
                top.recycle()
            }
        }
        return result
    }


    eachChild(node: AutoJs.UiObject | null, callback: (node: AutoJs.UiObject, index: number) => void) {
        if (!node) return;
        const childCount = node.childCount()
        for (let i = 0; i < childCount; i++) {
            const child = node.child(i)
            if (child) {
                callback(child, i)
            }
        }
    }

    eachDeepChild(node: AutoJs.UiObject | null, callback: (node: AutoJs.UiObject) => void) {
        if (!node) return;
        const childCount = node.childCount()
        for (let i = 0; i < childCount; i++) {
            const child = node.child(i)
            if (child) {
                callback(child)
            }
        }
    }

    getSid(node: AutoJs.UiObject | undefined | null): string {
        if (!node) return ""
        const id = node.id()
        if (id) return id.split('/').pop()!
        return ""
    }

    createRect(obj: any): AutoJs.Rect {
        return obj as AutoJs.Rect;
    }


    create() {
        return new AstContext(this.d, this)
    }

    isTop() {
        return !this._parent
    }
}